package typedefault

import (
	"bufio"
	"codejy/globalfunc"
	"codejy/store"
	"codejy/structdef"
	"codejy/utils"
	"fmt"
	"io"
	"math"
	"os"
	"strconv"
)

// key 实例地址,value 值

type TypeFile struct {
	Addr         string
	ThreadStore  *structdef.ThreadStore
	ReadPath     string
	ReadPathSwap string
	WritePath    string

	//文件读取对象指针
	FileRead     *os.File
	FileLineRead *os.File
	//文件读取对象指针
	FileReadSwap     *os.File
	FileLineReadSwap *os.File
	FileReadLineTemp *os.File
	//文件行读取对象
	FileLineReadReader *bufio.Reader
	//临时文件读取对象指针
	FileReadTemp *os.File
	//临时文件路径
	FileReadTempPath string
	//文件信息对象
	ReadFileInfo os.FileInfo
	//读文件指针,用于读取文件时候的指针
	ReadFilePointer int64

	//读文件模式(当前)
	ReadModel string

	//文件写入对象指针
	FileWrite       *os.File
	FileWriteAppend *os.File
	FileWriteSwap   *os.File
	//写文件指针,用于写入文件时候的指针
	WriteFilePointer int64
	//写文件模式(当前)
	WriteModel string
	//缓存写入开关,只允许一次写入
	WriteTempCache bool
	//读文件最大字节数
	ReadMaxBytes int64
	//读取文件行数最大值
	ReadFileLineMax int
}

// 创建CreateFile ,读文件必须传入,写文件可选

func (a *TypeFile) CreateTypeDefault(addr string, threadStore *structdef.ThreadStore, parameter ...string) {
	newFile := &TypeFile{
		Addr:         addr,
		ThreadStore:  threadStore,
		ReadPath:     parameter[0],
		ReadPathSwap: parameter[1],
	}
	//判断读写不能是同一个文件
	if newFile.ReadPath == newFile.WritePath {
		panic("读写不能是同一个文件")
	}
	//判断读文件是否是空
	if newFile.ReadPath != "" {
		//创建文件读取对象
		fileRead, err := os.OpenFile(newFile.ReadPath, os.O_RDONLY, 0666)
		if err != nil {
			panic(err)
		}
		newFile.FileRead = fileRead
		fileLineRead, err := os.OpenFile(newFile.ReadPath, os.O_RDONLY, 0666)
		newFile.FileLineRead = fileLineRead
		newFile.FileLineReadReader = bufio.NewReader(newFile.FileLineRead)
		fileInfo, err := newFile.FileRead.Stat()
		if err != nil {
			panic("读文件路径错误,或者文件不存在,请仔细检查")
		}
		newFile.ReadFileInfo = fileInfo
		//创建临时文件读取对象
		temp, err := os.CreateTemp("", fileInfo.Name())
		if err != nil {
			panic(err)
		}
		newFile.FileReadTemp = temp
		newFile.FileReadTempPath = temp.Name()
		//行操作,和字节操作进行隔离
		fileReadLineTemp, err := os.OpenFile(newFile.FileReadTempPath, os.O_RDONLY, 0666)
		newFile.FileReadLineTemp = fileReadLineTemp

		//最大行数
		newFile.ReadFileLineMax = utils.GetFileLine(newFile.ReadPath)
		newFile.ReadMaxBytes = fileInfo.Size()

	}
	//判断写文件是否是空
	if newFile.WritePath != "" {
		//创建文件写入对象
		fileWrite, err := os.OpenFile(newFile.WritePath, os.O_WRONLY|os.O_CREATE, 0755)
		if err != nil {
			panic(err)
		}
		newFile.FileWrite = fileWrite
		fileWriteAppend, _ := os.OpenFile(newFile.WritePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0755)
		newFile.FileWriteAppend = fileWriteAppend

	}
	newFile.FileModel("0", "1") //默认正向读取,追加写模式
	store.AddTypeDefaultInstanceStore(threadStore, a.GetTypeName(), addr, newFile)
}
func (a *TypeFile) GetData() interface{} {
	return nil
}
func (a *TypeFile) ToStr() string {
	return ""
}

func (a *TypeFile) ParameterProcessing(parameter []string) []string {
	for i := range parameter {
		StrToHandle(&parameter[i])
	}
	return parameter
}

// read   0 是正向读取(默认),  1是反向读取(反向读取第一次会慢很多,并且不允许文件的大于2G)
// wite   0 是追加模式     ,1 是正向覆盖从第0行开始,   每次切换所有写指针都会重置 ,重置指针
func (a *TypeFile) FileModel(read string, write string) {

	if read != "" {
		if read == "0" {
			//将反向读取切换为正向读取
			if a.FileReadSwap != nil {
				a.FileRead = a.FileReadSwap
				a.FileLineRead = a.FileLineReadSwap
				a.ReadPath = a.ReadPathSwap
			}
			a.ReadModel = "0"

		} else {

			if !a.WriteTempCache { //读文件只允许一次反向读取缓存写入本地
				//判断文件不能超过2G
				if a.ReadFileInfo.Size() > 2147483648 {
					panic("文件大于2G,无法反向读取")
				}
				//按行读取文件行
				var line []string
				for {
					lineRead, _, err := a.FileLineReadReader.ReadLine()
					if err != nil {
						break
					}
					line = append(line, string(lineRead))
				}
				//反向输出到临时文件中,按行
				for i := len(line) - 1; i >= 0; i-- {
					a.FileReadTemp.WriteString(line[i] + "\n")
				}

				a.WriteTempCache = true
			}
			//保留正向读取的文件对象
			a.FileReadSwap = a.FileRead
			a.FileLineReadSwap = a.FileLineRead
			a.ReadPathSwap = a.ReadPath //保留正向读取的文件路径

			//将缓存文件对象和路径设置为当前读取对象
			a.FileRead = a.FileReadTemp
			a.ReadPath = a.FileReadTempPath
			a.FileLineRead = a.FileReadLineTemp
			//设置当前读取模式为反向读取
			a.ReadModel = "1"

		}
		//重置指针
		a.ReadFilePointer = 0

		//切换到新的位置
		a.FileRead.Seek(0, 0)
		a.FileLineRead.Seek(0, 0)
		//重置行读取对象
		a.FileLineReadReader = bufio.NewReader(a.FileRead)
	}

	if write != "" {
		if write == "0" {
			if a.FileWriteSwap != nil {
				a.FileWrite = a.FileWriteSwap
			}
			a.WriteModel = "0"
			a.FileWrite.Seek(0, 0)
		} else { //追加方式
			a.WriteModel = "1"
			a.FileWriteSwap = a.FileWrite
			a.FileWrite = a.FileWriteAppend
		}
		//重置文件写入指针
		a.WriteFilePointer = 0
	}

}

// rbyte, rline, wbyte, wline 同一时间只能有一个有值
func (a *TypeFile) FileSkip(rbyte, wbyte, rline, wline string) {

	//如果写模式是1,则不允许跳过
	if a.WriteModel == "1" && (wbyte != "" || wline != "") {
		panic("写模式是1,追加模式,不允许写跳过")
	}

	// rbyte, rline, wbyte, wline 只能有一个有值否则报错
	var num = 0
	var array = []string{rbyte, wbyte, rline, wline}
	for i := range array {
		if array[i] != "" {
			num++
		}
	}
	if num > 1 {
		panic("rbyte, rline, wbyte, wline 只能有一个有值,其余必须为空")
	}

	if rbyte != "" {
		atoi, _ := strconv.ParseInt(rbyte, 10, 64)
		a.ReadFilePointer = a.ReadFilePointer + atoi
		_, err := a.FileRead.Seek(a.ReadFilePointer, 0)
		if err != nil {
			panic(err)
		}
		return
	}
	if wbyte != "" {
		atoi, _ := strconv.ParseInt(rbyte, 10, 64)
		a.WriteFilePointer = a.WriteFilePointer + atoi
		_, err := a.FileWrite.Seek(atoi, 0)
		if err != nil {
			panic(err)
		}
		return
	}

	if rline != "" {
		atoi, _ := strconv.Atoi(rline)

		for i := 0; i < atoi; i++ {
			_, _, err := a.FileLineReadReader.ReadLine()
			if err != nil {
				panic(err)
			}
		}
		return
	}
	if wline != "" {
		atoi, _ := strconv.Atoi(wline)
		//获取
		lineByte := utils.GetFileLineByte(a.WritePath, atoi)
		//从指定行位置写入
		_, err := a.FileWrite.Seek(lineByte, 0)
		if err != nil {
			panic(err)
		}
		return
	}

}

// 读取指定字节,返回字节地址
func (a *TypeFile) FileReadByte(num string) string {

	if num == "" {
		num = "1"
	}
	if num == "all" {
		num = strconv.FormatInt(a.ReadMaxBytes, 10)
	}
	//转换为int64
	atoi, _ := strconv.ParseInt(num, 10, 64)
	//读取指定字节
	var b = make([]byte, atoi)
	_, err := a.FileRead.Read(b)
	if err == io.EOF {
		return ""
	}
	b = utils.GetActualBytes(b) //去除空字节
	a.FileSkip(num, "", "", "")
	return globalfunc.ExecuteFunc("TransitionBytes", b, a.ThreadStore).(string)

}

// 读取指定行数,返回List
func (a *TypeFile) FileReadLines(num string) string {

	if num == "" {
		num = "1"
	}
	if num == "all" {
		num = strconv.Itoa(math.MaxInt)
	}
	//转换为int64
	atoi, _ := strconv.ParseInt(num, 10, 64)
	//读取指定行数
	var str []string
	r := a.FileLineReadReader
	for i := 0; i < int(atoi); i++ {
		//读取一行
		line, _, err := r.ReadLine()
		if err == io.EOF {
			break
		}
		str = append(str, string(line))

	}
	if len(str) == 0 {
		return ""
	}

	return globalfunc.ExecuteFunc("SliceToList", str, a.ThreadStore).(string)
}
func (a *TypeFile) FileReadLine() string {

	//读取指定行数
	line, _, err := a.FileLineReadReader.ReadLine()
	if err == io.EOF {
		return ""
	}

	return StringToStr(string(line))
}

// 写入(自动识别,字节和字符)
func (a *TypeFile) FileWriteByte(addr string) {
	bytes, p1 := GetTypeDefaultValue[[]byte](a.ThreadStore, "Bytes", addr)
	if p1 {
		//写入指定字节
		_, err := a.FileWrite.Write(bytes)
		if err != nil {
			panic(err)
		}
		if a.WriteModel != "1" {
			atoi, _ := strconv.ParseInt(strconv.Itoa(len(bytes)), 10, 64)
			a.WriteFilePointer = a.WriteFilePointer + atoi
			//下次写入指针位置
			a.FileSkip("", strconv.FormatInt(a.WriteFilePointer, 10), "", "")
		}
		return
	}
	lines, p2 := GetTypeDefaultValue[[]string](a.ThreadStore, "List", addr)
	if p2 {
		//按行写入
		for i := range lines {
			_, err := a.FileWrite.WriteString(lines[i] + "\n")
			if err != nil {
				panic(err)
			}
		}

	}
	//如果都不是,就是字符串
	if !p1 && !p2 {
		//写入指定字符串
		_, err := a.FileWrite.WriteString(addr)
		if err != nil {
			panic(err)
		}
	}
}

// 关闭文件
func (a *TypeFile) FileClose() {
	a.FileRead.Close()
	a.FileLineRead.Close()
	a.FileWrite.Close()
	a.FileReadTemp.Close()
	a.FileLineReadSwap.Close()
	a.FileWriteAppend.Close()
	a.FileWriteSwap.Close()

	//删除临时文件
	os.Remove(a.FileReadTempPath)
	utils.DebugLog("文件操作:", func() {
		fmt.Println(a.ReadPath + "读文件已关闭")
		fmt.Println(a.FileReadTempPath + "缓存文件已关闭")
		fmt.Println(a.WritePath + "写文件已关闭")
	})
}

func (a *TypeFile) GetReadModel() string {
	return a.ReadModel
}
func (a *TypeFile) GetWriteModel() string {
	return a.WriteModel
}

func (a *TypeFile) GetReadBytesNum() string {
	return strconv.FormatInt(a.ReadFilePointer, 10)
}

func (a *TypeFile) GetWriteBytesNum() string {
	return strconv.FormatInt(a.WriteFilePointer, 10)
}

func (a *TypeFile) GetReadFileSize() string {
	return strconv.FormatInt(a.ReadMaxBytes, 10)
}
func (a *TypeFile) GetReadFileLineMax() string {
	return strconv.Itoa(a.ReadFileLineMax)
}

func (f *TypeFile) GetDef() string {
	return `
		@CodeManual(def="new("File",$read="readFilePath",$write="writeFilePath")",des="创建文件操作实例,读写文件,参数可更具情况选择")
		type File{
			def init{
				core&CreateTypeDefault($read,$write)
			}
			//● rbyte跳过字节,wbyte跳过写字节,rline跳过行,wline跳过写行
			//● 每次读取都是接着上次读取,直到读取完毕返回""
			//● 跳过字段只能作用字节处理方式,跳过行只能作用行处理方式
			@CodeManual(def="skip($rbyte,$wbyte,$rline,$wline)",des="跳过指定字节或行数(数字),例:file.skip($rline=1)跳过一行")
			def skip{
				core&FileSkip($rbyte,$wbyte,$rline,$wline)
			}

			// 读写模式设置
 			// 每次切模式换都会重置对应模式的指针
			// read 0 是正向读取(默认),1是反向读取(反向读取第一次会慢很多,并且不允许文件的大于2G,只允许字符数据)  
			// wite 0 是正向写从第1行开始(存在就会被覆盖)     1是追加模式 (默认)
			@CodeManual(def="model($read,$write)",des="读写模式设置(每次切模式换都会重置对应模式的指针),例:file.model($read=1) 反向读取")
			def model{
				core&FileModel($read,$write)
			}	
			
			@CodeManual(def="read($1)",des="读取指定字节,例: file.read(10) 读取10个字节")
			def read{
				to core&FileReadByte($1)
			}

		
			@CodeManual(def="readlines($1)",des="读取指定行数,返回列表,例: file.readlines(10)读取10行")
			def readlines{
				to core&FileReadLines($1)
			}

			
			@CodeManual(def="readline()",des="每次读取一行,返回字符串,例: file.readlines(10)读取10行")
			def readline{
				to core&FileReadLine()
			}

			@CodeManual(def="write()",des="写入数据到文件,支持字节,字符串列表,字符串,例:file.write("123")写入123")
			def write{
				core&FileWriteByte($1)
			}

			@CodeManual(def="close()",des="关闭文件(必须的),关闭后不可再操作")
			def close{
				 core&FileClose()
			}

	
			@CodeManual(def="getReadModel()",des="获取读模式,返回0是正向读取,1是反向读取")
			def getReadModel{
				 to core&GetReadModel()
			}

			@CodeManual(def="getWriteModel()",des="获取写模式,返回0是正向写,1是追加写")
			def getWriteModel{
				 to core&GetWriteModel()
			}


		
			@CodeManual(def="getReadBytesNum()",des="获取累积读取的行数")
			def getReadBytesNum{
				 to core&GetReadBytesNum()
			}
		
			@CodeManual(def="getWriteBytesNum()",des="获取累积写入的字节数")
			def getWriteBytesNum{
				 to core&GetWriteBytesNum()
			}
		
			@CodeManual(def="getReadSumSize()",des="获取读取文件的大小")
			def  getReadSumSize{
				 to core&GetReadFileSize()
		    }

			@CodeManual(def="getReadSumLineNum()",des="获取读取文件的总行数")
			def  getReadSumLineNum{
				 to core&GetReadFileLineMax()	
			}

		}
	`
}

func init() {
	RegisterTypeDefault(&TypeFile{})
}

func (f *TypeFile) GetTypeName() string {
	return "File"
}
